
//  __  __         _          _         ___                        _   
// |  \/  |  __ _ | |_  _ __ (_)__  __ / _ \  _   _   __ _  _ __  | |_ 
// | |\/| | / _` || __|| '__|| |\ \/ /| | | || | | | / _` || '_ \ | __|
// | |  | || (_| || |_ | |   | | >  < | |_| || |_| || (_| || | | || |_ 
// |_|  |_| \__,_| \__||_|   |_|/_/\_\ \__\_\ \__,_| \__,_||_| |_| \__|
// 
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © MatrixQuantLabs
//
//@version=6
indicator(title="Stochastic Extreme Oscillator [MatrixQuantLabs]", format=format.price, precision=2, timeframe="", timeframe_gaps=true)

// Input parameters
periodK = input.int(14, title="%K Length", minval=1, group="Basic Settings丨基础设置")
smoothK = input.int(1, title="%K Smoothing", minval=1, group="Basic Settings丨基础设置")
periodD = input.int(3, title="%D Smoothing", minval=1, group="Basic Settings丨基础设置")
showSignals = input.bool(true, title="Show Peak and Trough Signals丨显示顶底信号", group="Basic Settings丨基础设置")

// Divergence settings
showDivergence = input.bool(false, title='Show Divergence丨显示背离', group='Divergence丨背离设置', tooltip='▶︎ Pivot Left: Number of bars to the left used to confirm a pivot high or low. Higher values detect more significant pivots but reduce signal frequency.\n▶︎ Pivot Right: Number of bars to the right required to confirm a pivot. Larger values increase confirmation reliability but add delay; smaller values are more sensitive and faster.\n▶︎ Min Range: Minimum number of bars between divergence points. Helps filter out signals that are too close and likely to be noise.\n▶︎ Max Range: Maximum number of bars allowed between divergence points. Limits how far back divergence signals can be detected.\n\n▶︎ 向左回溯：用于确认高点或低点的左侧回溯 K 线数量。数值越大，识别的拐点越重要，但信号出现频率会降低。\n▶︎ 向右回溯：用于确认拐点所需的右侧 K 线数量。数值越大，确认更可靠但信号延迟更高；数值越小，灵敏度更高、响应更快。\n▶︎ 最小范围：背离点之间的最小 K 线数量。用于过滤距离过近、噪音较大的背离信号。\n▶︎ 最大范围：背离点之间允许的最大 K 线数量。用于限制背离信号向历史回溯的范围。')
PivotLookbackLeft = input.int(5, title='Pivot Left丨向左回溯', minval=1, inline='Pivot', group='Divergence丨背离设置')
PivotLookbackRight = input.int(5, title='Pivot Right丨向右回溯', minval=1, inline='Pivot', group='Divergence丨背离设置')
MinLookbackRange = input.int(5, title='Min Range丨最小范围', minval=1, inline='Range', group='Divergence丨背离设置')
MaxLookbackRange = input.int(60, title='Max Range丨最大范围', minval=1, inline='Range', group='Divergence丨背离设置')

// Overbought area
OverboughtLine1 = hline(100, color=color.new(color.green, 0), linestyle=hline.style_solid, title='Overbought Upper Band丨超买上轨')
OverboughtLine2 = hline(90, color=color.new(color.green, 25), linestyle=hline.style_dashed, title='Overbought Middle Band丨超买中轨')
OverboughtLine3 = hline(80, color=color.new(color.green, 25), linestyle=hline.style_dotted, title='Overbought Lower Band丨超买下轨')
// Oversold area
OversoldLine3 = hline(20, color=color.new(color.red, 25), linestyle=hline.style_dotted, title='Oversold Upper Band丨超卖上轨')
OversoldLine2 = hline(10, color=color.new(color.red, 25), linestyle=hline.style_dashed, title='Oversold Middle Band丨超卖中轨')
OversoldLine1 = hline(0, color=color.new(color.red, 0), linestyle=hline.style_solid, title='Oversold Lower Band丨超卖下轨')

hline(50, color=color.new(color.gray, 0), linestyle=hline.style_dotted, title='Zero Line丨零轴线')

// Calculation indicators
k = ta.sma(ta.stoch(close, high, low, periodK), smoothK)
d = ta.sma(k, periodD)

// Overbought zone filling logic (90-100 range)
overboughtRed = k < d and d >= 80 and d <= 100
overboughtOrange = d > 90 and not overboughtRed
overboughtYellow = k > 80 and not overboughtRed and not overboughtOrange
overboughtColor = overboughtRed ? color.new(color.red, 0) : overboughtOrange ? color.new(color.red, 40) : overboughtYellow ? color.new(color.red, 80) : na
fill(OverboughtLine2, OverboughtLine1, color=overboughtColor, title='Overbought Fill丨超买区填充')

// Oversold zone filling logic (0-10 range)
oversoldGreen = k > d and d >= 0 and d <= 20
oversoldBlue = d < 10 and not oversoldGreen
oversoldYellow = k < 20 and not oversoldGreen and not oversoldBlue
oversoldColor = oversoldGreen ? color.new(color.green, 0) : oversoldBlue ? color.new(color.green, 40) : oversoldYellow ? color.new(color.green, 80) : na
fill(OversoldLine1, OversoldLine2, color=oversoldColor, title='Oversold Fill丨超卖区填充')

dColor = d >= 60 ? color.new(#00ffaa, 0) : d >= 40 ? color.new(color.gray, 0) : color.new(#ff3355, 0)
plot(k, title="%K", color=#ffffff, style=plot.style_line, display=display.none)
dPlot = plot(d, title="%D", color=dColor, style=plot.style_line)

zeroLinePlot = plot(50, color=na, display=display.none, editable=false)
fill(dPlot, zeroLinePlot, 100, 50, top_color=color.new(#00ffaa, 0), bottom_color=color.new(#00ffaa, 100), title="Above Zero Fill丨零轴以上填充")
fill(dPlot, zeroLinePlot, 50, 0, top_color=color.new(#ff3355, 100), bottom_color=color.new(#ff3355, 0), title="Below Zero Fill丨零轴以下填充")

bearishCross = showSignals and ta.crossunder(k, d) and d >= 80 and d <= 100
bullishCross = showSignals and ta.crossover(k, d) and d >= 0 and d <= 20
plotshape(bearishCross, title='Bearish Signal丨看跌信号', style=shape.triangledown, location=location.top, color=color.new(#ffffff, 0), display=display.pane, editable=false)
plotshape(bullishCross, title='Bullish Signal丨看涨信号', style=shape.triangleup, location=location.bottom, color=color.new(#ffffff, 0), display=display.pane, editable=false)

// Divergence detection
bearColor = color.red
bullColor = color.green
textColor = color.white
noneColor = color.new(color.white, 100)

// Range checking function
_inRange(cond) =>
    bars = ta.barssince(cond)
    MinLookbackRange <= bars and bars <= MaxLookbackRange

// Initialize variables (in the global scope)
plFound = false
phFound = false
bullCond = false
bearCond = false

stochLBR = d[PivotLookbackRight]

if showDivergence
    plFound := not na(ta.pivotlow(d, PivotLookbackLeft, PivotLookbackRight))
    stochHL = stochLBR > ta.valuewhen(plFound, stochLBR, 1) and _inRange(plFound[1])
    lowLBR = low[PivotLookbackRight]
    priceLL = lowLBR < ta.valuewhen(plFound, lowLBR, 1)
    bullCond := priceLL and stochHL and plFound

    phFound := not na(ta.pivothigh(d, PivotLookbackLeft, PivotLookbackRight))
    stochLH = stochLBR < ta.valuewhen(phFound, stochLBR, 1) and _inRange(phFound[1])
    highLBR = high[PivotLookbackRight]
    priceHH = highLBR > ta.valuewhen(phFound, highLBR, 1)
    bearCond := priceHH and stochLH and phFound

// Draw divergence
plot(plFound ? stochLBR : na, offset=-PivotLookbackRight, title="Bullish Divergence Line丨看涨背离线", color=bullCond ? bullColor : noneColor, display=display.pane)
plotshape(bullCond ? stochLBR : na, offset=-PivotLookbackRight, title="Bullish Divergence Signal丨看涨背离信号", text="▲", style=shape.labelup, location=location.absolute, color=bullColor, textcolor=textColor, display=display.pane)
plot(phFound ? stochLBR : na, offset=-PivotLookbackRight, title="Bearish Divergence Line丨看跌背离线", color=bearCond ? bearColor : noneColor, display=display.pane)
plotshape(bearCond ? stochLBR : na, offset=-PivotLookbackRight, title="Bearish Divergence Signal丨看跌背离信号", text="▼", style=shape.labeldown, location=location.absolute, color=bearColor, textcolor=textColor, display=display.pane)

// Signal alert
alertcondition(bullishCross, title='Stoch Bullish Signal丨随机指标看涨信号', message='✅ Stochastic indicator enters oversold zone.')
alertcondition(bearishCross, title='Stoch Bearish Signal丨随机指标看跌信号', message='❌ Stochastic indicator enters overbought  zone.')

// Divergence alert
alertcondition(bullCond, title='Stoch Bullish Divergence丨随机指标看涨背离', message='📈 Stochastic Bullish Divergence: Price lower low but indicator higher low')
alertcondition(bearCond, title='Stoch Bearish Divergence丨随机指标看跌背离', message='📉 Stochastic Bearish Divergence: Price higher high but indicator lower high')

// End

